package com.liuya.safe.entitle.service.impl;

import java.io.Serializable;
import java.util.*;

import com.liuya.common.I18N;
import com.liuya.common.MyHashMap;
import com.liuya.common.SpringUtils;
import com.liuya.common.StringUtil;
import com.liuya.db.util.DBUtil;
import com.liuya.entitle.*;
import com.liuya.safe.entitle.dao.SafeDecisionEntitlementMapper;
import com.liuya.safe.entitle.dao.SafeQueryEntitlementMapper;
import com.liuya.safe.entitle.service.EntitleManagerService;
import com.liuya.safe.entitle.service.PrivilegeManagerService;
import com.liuya.safe.exception.AuthException;
import com.liuya.safe.model.*;
import com.liuya.safe.policy.service.BusinessDataManagerService;
import com.liuya.safe.policy.service.QueryManagerService;
import com.liuya.safe.policy.service.UserCategoryManagerService;
import com.liuya.safe.user.service.UserRoleManagerService;
import com.liuya.safe.util.Constants;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.liuya.common.exception.EntityExistException;

import java.sql.SQLException;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import com.liuya.db.DBLevelException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service("entitleManagerService")
public class EntitleManagerServiceImpl implements EntitleManagerService, Serializable {
	private static Log log = LogFactory.getLog(EntitleManagerServiceImpl.class);

	private String appName;

    @Autowired
	@Qualifier("queryManagerService")
    QueryManagerService queryManager;
	@Autowired
	@Qualifier("userCategoryManagerService")
	UserCategoryManagerService userCategoryManager;
    @Autowired
	@Qualifier("businessDataManagerService")
	BusinessDataManagerService businessDataManager;
	@Autowired
	@Qualifier("privilegeManagerService")
	PrivilegeManagerService privilegeManager;
    @Autowired
	@Qualifier("safeDecisionEntitlementMapper")
	SafeDecisionEntitlementMapper decisionEntitlementMapper;
    @Autowired
	@Qualifier("safeQueryEntitlementMapper")
    SafeQueryEntitlementMapper queryEntitlementMapper;

	public EntitleManagerServiceImpl() {
	}

	public EntitleManagerServiceImpl(String appName) {
		this.appName = appName;
	}

	public Collection getDecisionEntitlements(String pvlgId) {
		Map param = new HashMap();
		param.put("privilegeId", pvlgId);
		Collection entitlements = decisionEntitlementMapper.selectAll(param);
		for (Iterator iter = entitlements.iterator(); iter.hasNext();) {
			SafeDecisionEntitlement entitle = (SafeDecisionEntitlement) iter.next();
			SafeUserCategory userCategory = userCategoryManager.getUserCategory(entitle.getUserCategoryId());
			SafeBusinessData businessData = businessDataManager.getBusinessData(entitle.getBusinessDataId());

			entitle.setUserCategory(userCategory);
			entitle.setBusinessData(businessData);
		}

		return entitlements;
	}

	public Collection getQueryEntitlements(String pvlgId) {
        Map param = new HashMap();
        param.put("privilegeId", pvlgId);
		Collection entitlements = queryEntitlementMapper.selectAll(param);

		for (Iterator iter = entitlements.iterator(); iter.hasNext();) {
			SafeQueryEntitlement entitle = (SafeQueryEntitlement) iter.next();
			SafeUserCategory userCategory = userCategoryManager.getUserCategory(entitle.getUserCategoryId());
			SafeQuery query = queryManager.getQuery(entitle.getQueryId());

			entitle.setUserCategory(userCategory);
			entitle.setQuery(query);
		}

		return entitlements;
	}

	public Decision permit(Locale locale, String privilegeId, SafeUser user, Map context) {
		Collection decisionEntitlements = getDecisionEntitlements(privilegeId);
		DecisionEntitlementTestResult result = testDecisionEntitlement(locale, privilegeId, new ArrayList(decisionEntitlements), user, context);
		Decision decision = null;
		if (result.isFailed()) {
			decision = new Decision();
			decision.setPermit(false);
			decision.setDenyReason(result.getErrorMessage());
		} else {
			decision = result.getDecision();
		}

		return decision;
	}

	/**
	 * Caculate rules by order. Return first match result. If no rule is
	 * matched, return deny.
	 *
	 * @param decisionEntitlements
	 * @param user
	 * @param context
	 */
	private DecisionEntitlementTestResult decisionFirstApplicable(List decisionEntitlements, SafeUser user, Map context) {
		DecisionEntitlementTestResult result = new DecisionEntitlementTestResult();

		Decision decision = new Decision();
		String denyReason = "";
		for (Iterator iter = decisionEntitlements.iterator(); iter.hasNext();) {
			SafeDecisionEntitlement entitlement = (SafeDecisionEntitlement) iter.next();
			SafeUserCategory userCategory = userCategoryManager.getUserCategory(entitlement.getUserCategoryId());
			// test userCategory
			UserCategoryTestResult userCategoryTestResult = userCategory.test(user, context, queryManager);
			result.getUserCategoryTestResults().add(userCategoryTestResult);

			if (userCategoryTestResult.isFailed()) {
				result.setFailed(true);
				result.setErrorMessage(userCategoryTestResult.getErrorMessage());
				result.getBusinessDataTestResults().add(new BusinessDataTestResult());
				return result;
			}

			if (userCategoryTestResult.isValid()) {
				SafeBusinessData businessData = businessDataManager.getBusinessData(entitlement.getBusinessDataId());
				// test businessData
				BusinessDataTestResult businessDataTestResult = businessData.test(user, context, queryManager);
				result.getBusinessDataTestResults().add(businessDataTestResult);
				if (businessDataTestResult.isFailed()) {
					result.setFailed(true);
					result.setErrorMessage(businessDataTestResult.getErrorMessage());
					return result;
				}

				if (businessDataTestResult.isValid()) {
					if (entitlement.getEffect().equals(SafeDecisionEntitlement.DENY)) {
						decision.setPermit(false);
						decision.setDenyReason(entitlement.getDenyReason());
					} else if (entitlement.getEffect().equals(SafeDecisionEntitlement.PERMIT)) {
						decision.setPermit(true);
					}
					result.setFailed(false);
					result.setDecision(decision);
					return result;
				} else {
					if (entitlement.getEffect().equals(SafeDecisionEntitlement.PERMIT)) {
						// append deny reason
						if (!StringUtil.isEmpty(denyReason)) {
							denyReason = denyReason + "\n";
						}
						denyReason = denyReason + entitlement.getDenyReason();
					}
				}
			} else {
				result.getBusinessDataTestResults().add(new BusinessDataTestResult());
			}
		}

		// no rule is matched
		decision.setPermit(false);
		if (denyReason.equals("")) {
			decision.setDenyReason(Decision.DEFAULT_DENY_REASON);
		} else {
			decision.setDenyReason(denyReason);
		}
		result.setFailed(false);
		result.setDecision(decision);
		return result;
	}

	/**
	 * Deny override. Caculate all rules, return deny if any matched rule result
	 * is deny. If all matched rules results are permit, then return permit. If
	 * no rule is matched, return deny.
	 *
	 *
	 * @param decisionEntitlements
	 * @param user
	 * @param context
	 */
	private DecisionEntitlementTestResult decisionOrderedDenyOverrides(List decisionEntitlements, SafeUser user, Map context) {
		DecisionEntitlementTestResult result = new DecisionEntitlementTestResult();

		Decision decision = new Decision();
		String denyReason = "";

		for (Iterator iter = decisionEntitlements.iterator(); iter.hasNext();) {
			SafeDecisionEntitlement entitlement = (SafeDecisionEntitlement) iter.next();
			SafeUserCategory userCategory = userCategoryManager.getUserCategory(entitlement.getUserCategoryId());
			// test userCategory
			UserCategoryTestResult userCategoryTestResult = userCategory.test(user, context, queryManager);
			result.getUserCategoryTestResults().add(userCategoryTestResult);
			if (userCategoryTestResult.isFailed()) {
				result.setFailed(true);
				result.setErrorMessage(userCategoryTestResult.getErrorMessage());
				return result;
			}

			if (entitlement.getEffect().equals(SafeDecisionEntitlement.PERMIT)) {
				if (!denyReason.equals("")) {
					denyReason = denyReason + "\n";
				}
				denyReason = denyReason + entitlement.getDenyReason();
			}

			if (userCategoryTestResult.isValid()) {
				SafeBusinessData businessData = businessDataManager.getBusinessData(entitlement.getBusinessDataId());
				// test businessData
				BusinessDataTestResult businessDataTestResult = businessData.test(user, context, queryManager);
				result.getBusinessDataTestResults().add(businessDataTestResult);
				if (businessDataTestResult.isFailed()) {
					result.setFailed(true);
					result.setErrorMessage(businessDataTestResult.getErrorMessage());
					return result;
				}

				if (businessDataTestResult.isValid()) {
					if (entitlement.getEffect().equals(SafeDecisionEntitlement.DENY)) {
						decision.setPermit(false);
						decision.setDenyReason(entitlement.getDenyReason());
						result.setFailed(false);
						result.setDecision(decision);
						return result;
					} else if (entitlement.getEffect().equals(SafeDecisionEntitlement.PERMIT)) {
						{
							decision.setPermit(true);
						}
					}
				}
			}
		}

		if (!decision.isPermit()) {
			if ("".equals(denyReason)) {
				decision.setDenyReason(Decision.DEFAULT_DENY_REASON);
			} else {
				decision.setDenyReason(denyReason);
			}
		}
		result.setFailed(false);
		result.setDecision(decision);
		return result;
	}

	/**
	 * Permit override. Caculate all rules, return permit if any matched rule
	 * result is permit. If all matched rules results are deny, then return
	 * deny. If no rule is matched, return deny.
	 *
	 * @param decisionEntitlements
	 * @param user
	 * @param context
	 */
	private DecisionEntitlementTestResult decisionOrderedPermitOverrides(List decisionEntitlements, SafeUser user, Map context) {
		DecisionEntitlementTestResult result = new DecisionEntitlementTestResult();

		Decision decision = new Decision();
		String denyReason = "";

		for (Iterator iter = decisionEntitlements.iterator(); iter.hasNext();) {
			SafeDecisionEntitlement entitlement = (SafeDecisionEntitlement) iter.next();
			SafeUserCategory userCategory = userCategoryManager.getUserCategory(entitlement.getUserCategoryId());
			// test userCategory
			UserCategoryTestResult userCategoryTestResult = userCategory.test(user, context, queryManager);
			result.getUserCategoryTestResults().add(userCategoryTestResult);
			if (userCategoryTestResult.isFailed()) {
				result.setFailed(true);
				result.setErrorMessage(userCategoryTestResult.getErrorMessage());
				return result;
			}

			if (entitlement.getEffect().equals(SafeDecisionEntitlement.PERMIT)) {
				if (!denyReason.equals("")) {
					denyReason = denyReason + "\n";
				}
				denyReason = denyReason + entitlement.getDenyReason();
			}

			if (userCategoryTestResult.isValid()) {
				//
				SafeBusinessData businessData = businessDataManager.getBusinessData(entitlement.getBusinessDataId());
				// test businessData
				BusinessDataTestResult businessDataTestResult = businessData.test(user, context, queryManager);
				result.getBusinessDataTestResults().add(businessDataTestResult);
				if (businessDataTestResult.isFailed()) {
					result.setFailed(true);
					result.setErrorMessage(businessDataTestResult.getErrorMessage());
					return result;
				}

				if (businessDataTestResult.isValid()) {
					if (entitlement.getEffect().equals(SafeDecisionEntitlement.PERMIT)) {
						decision.setPermit(true);
						result.setFailed(false);
						result.setDecision(decision);
						return result;
					} else if (entitlement.getEffect().equals(SafeDecisionEntitlement.DENY) && decision.getDenyReason() == null) {
						{
							decision.setPermit(false);
							decision.setDenyReason(entitlement.getDenyReason());
						}
					}
				}
			}
		}

		decision.setPermit(false);
		if (decision.getDenyReason() == null) {
			if (denyReason.equals("")) {
				decision.setDenyReason(Decision.DEFAULT_DENY_REASON);
			} else {
				decision.setDenyReason(denyReason);
			}
		}
		result.setFailed(false);
		result.setDecision(decision);
		return result;
	}

	/**
	 * Execute Logic:
	 * <ol>
	 * <li>Which rule the user is first matched;</li>
	 * <li>Execute the matched rule's query;</li>
	 * <li>Return query result.</li>
	 * </ol>
	 */
	public QueryResult query(String pvlgId, SafeUser user, Map ctx) {
		if (!hasPrivilege(user, pvlgId)) {
			return new QueryResult();
		}

		SafeQuery query = findQueryMatched(pvlgId, user, ctx);
		if (query == null) {
			return new QueryResult();
		} else {
			return query.execute(user, ctx);
		}
	}

	public SafeDecisionEntitlement addDecisionEntitlement(SafeDecisionEntitlement decisionEntitlement) throws EntityExistException {
		decisionEntitlement.setId(newDecisionEntitlementId());
        decisionEntitlementMapper.insert(decisionEntitlement);

		SafeUserCategory userCategory = userCategoryManager.getUserCategory(decisionEntitlement.getUserCategoryId());
		SafeBusinessData businessData = businessDataManager.getBusinessData(decisionEntitlement.getBusinessDataId());

		decisionEntitlement.setBusinessData(businessData);
		decisionEntitlement.setUserCategory(userCategory);
		return decisionEntitlement;
	}

	private String newDecisionEntitlementId() {
		try {
			return DBUtil.getSequenceNextVal("decision_entitlement_id").toString();
		} catch (SQLException e) {
			throw new DBLevelException(e);
		}
	}

	private String newQueryEntitlementId() {
		try {
			return DBUtil.getSequenceNextVal("query_entitlement_id").toString();
		} catch (SQLException e) {
			throw new DBLevelException(e);
		}
	}

	public SafeQueryEntitlement addQueryEntitlement(SafeQueryEntitlement queryEntitlement) throws EntityExistException {
		queryEntitlement.setId(newQueryEntitlementId());
        queryEntitlementMapper.insert(queryEntitlement);

		SafeUserCategory userCategory = userCategoryManager.getUserCategory(queryEntitlement.getUserCategoryId());
		SafeQuery query = queryManager.getQuery(queryEntitlement.getQueryId());

		queryEntitlement.setUserCategory(userCategory);
		queryEntitlement.setQuery(query);

		return queryEntitlement;
	}

	public void deleteDecisionEntitlement(String decisionEntitlementId) {
        decisionEntitlementMapper.deleteByPrimaryKey(decisionEntitlementId);
	}

	public void deleteQueryEntitlement(String queryEntitlementId) {
		queryEntitlementMapper.deleteByPrimaryKey(queryEntitlementId);
	}

	public void updateDecisionEntitlement(SafeDecisionEntitlement decisionEntitlement) throws EntityExistException {
        decisionEntitlementMapper.updateByPrimaryKey(decisionEntitlement);
	}

	public void updateQueryEntitlement(SafeQueryEntitlement queryEntitlement) throws EntityExistException {
        queryEntitlementMapper.updateByPrimaryKey(queryEntitlement);
	}

	public void deleteCascadeEntitlementByUserCategory(String userCategoryId) {
		SafeDecisionEntitlement decisionHint = new SafeDecisionEntitlement();
		decisionHint.setUserCategoryId(userCategoryId);
        Map param = new MyHashMap();
        param.put("userCategoryId", userCategoryId);

        decisionEntitlementMapper.deleteBy(param);
		queryEntitlementMapper.deleteBy(param);
	}

	public void deleteCascadeEntitlementByBusinessData(String businessDataId) {
        Map param = new MyHashMap();
        param.put("businessDataId", businessDataId);
        decisionEntitlementMapper.deleteBy(param);
	}

	public void deleteCascadeEntitlementByQuery(String queryId) {
        Map param = new MyHashMap();
        param.put("queryId", queryId);
        queryEntitlementMapper.deleteBy(param);
	}

	private void deleteEntitlementsByPrivilege(String privilegeId) {
        Map param = new MyHashMap();
        param.put("privilegeId", privilegeId);
        decisionEntitlementMapper.deleteBy(param);
        queryEntitlementMapper.deleteBy(param);
	}

	public QueryResult query(String pvlgId, SafeUser user, Map ctx, CustomizedWhere where) {
		if (!hasPrivilege(user, pvlgId)) {
			return new QueryResult();
		}

		SafeQuery query = findQueryMatched(pvlgId, user, ctx);
		if (query == null) {
			return new QueryResult();
		} else {
			return query.execute(user, ctx, where);
		}
	}

	private SafeQuery findQueryMatched(String pvlgId, SafeUser user, Map ctx) {
		Collection queryEntitlements = getQueryEntitlements(pvlgId);

		// return the query of first matched rule
		for (Iterator iter = queryEntitlements.iterator(); iter.hasNext();) {
			SafeQueryEntitlement entitlement = (SafeQueryEntitlement) iter.next();
			SafeUserCategory userCategory = userCategoryManager.getUserCategory(entitlement.getUserCategoryId());

			UserCategoryTestResult userCategoryTestResult = userCategory.test(user, ctx, queryManager);

			if (userCategoryTestResult.isFailed()) {
				String errorMessage = userCategoryTestResult.getErrorMessage();
				log.error(errorMessage);
				throw new AuthException(errorMessage);
			}

			if (userCategoryTestResult.isValid()) {
				return entitlement.getQuery();
			}
		}
		return null;
	}

	/**
	 * Find query of first matched rule.
	 *
	 * @return In two suituation returns null. 1, Exception happens when test
	 *         usercategory; 2, No rule is matched. In these suituations, set
	 *         result attributes: result.isFailed=true, errorMessage
	 */
	private SafeQuery findQueryMatched(Locale locale, Collection queryEntitlements, SafeUser user, Map ctx, QueryEntitlementTestResult result) {
		for (Iterator iter = queryEntitlements.iterator(); iter.hasNext();) {
			SafeQueryEntitlement entitlement = (SafeQueryEntitlement) iter.next();
			SafeUserCategory userCategory = userCategoryManager.getUserCategory(entitlement.getUserCategoryId());

			UserCategoryTestResult userCategoryTestResult = userCategory.test(user, ctx, queryManager);
			result.getUserCategoryTestResults().add(userCategoryTestResult);

			if (userCategoryTestResult.isFailed()) {
				result.setFailed(true);
				result.setErrorMessage(userCategoryTestResult.getErrorMessage());
				return null;
			}

			if (userCategoryTestResult.isValid()) {
				return queryManager.getQuery(entitlement.getQueryId());
			}
		}

		result.setFailed(true);
		result.setErrorMessage(I18N.getAuthMessage(locale, Constants.NO_QUERY_POLICY_MATCHED));
		return null;
	}

	public int queryCount(String pvlgId, SafeUser user, Map ctx) {
		if (!hasPrivilege(user, pvlgId)) {
			return 0;
		}

		SafeQuery query = findQueryMatched(pvlgId, user, ctx);
		if (query == null) {
			return 0;
		} else {
			return query.executeCount(user, ctx);
		}
	}

	public int queryCount(String pvlgId, SafeUser user, Map ctx, CustomizedWhere where) {
		if (!hasPrivilege(user, pvlgId)) {
			return 0;
		}

		SafeQuery query = findQueryMatched(pvlgId, user, ctx);
		if (query == null) {
			return 0;
		} else {
			return query.executeCount(user, ctx, where);
		}
	}

	public QueryResult query(String pvlgId, SafeUser user, Map ctx, int first, int max) {
		if (!hasPrivilege(user, pvlgId)) {
			return new QueryResult();
		}

		SafeQuery query = findQueryMatched(pvlgId, user, ctx);
		if (query == null) {
			return new QueryResult();
		} else {
			return query.execute(user, ctx, first, max);
		}
	}

	public QueryResult query(String pvlgId, SafeUser user, Map ctx, CustomizedWhere where, int first, int max) {
		if (!hasPrivilege(user, pvlgId)) {
			return new QueryResult();
		}

		SafeQuery query = findQueryMatched(pvlgId, user, ctx);
		if (query == null) {
			return new QueryResult();
		} else {
			return query.execute(user, ctx, where, first, max);
		}
	}

	@Transactional
	public void addEntitlements(String privilegeId, Collection decisionEntitlements, Collection queryEntitlements) {
		// delete all existing entitlements
		deleteEntitlementsByPrivilege(privilegeId);

		// add new decisionEntitlements
		addDecisionEntitlements(privilegeId, decisionEntitlements);

		// add new queryEntitlements
		addQueryEntitlements(privilegeId, queryEntitlements);
	}

	private void addDecisionEntitlements(String privilegeId, Collection decisionEntitlements) {
		Iterator itr = decisionEntitlements.iterator();
		while (itr.hasNext()) {
			SafeDecisionEntitlement item = (SafeDecisionEntitlement) itr.next();
			item.setPrivilegeId(privilegeId);
			item.setId(newDecisionEntitlementId());
		}
		batchSaveDecision(decisionEntitlements);
	}

	private void batchSaveDecision(Collection decisionEntitlements){
		Iterator itr = decisionEntitlements.iterator();
		while (itr.hasNext()) {
			SafeDecisionEntitlement item = (SafeDecisionEntitlement) itr.next();
			decisionEntitlementMapper.insert(item);
		}
	}

	private void addQueryEntitlements(String privilegeId, Collection queryEntitlements) {
		Iterator itr = queryEntitlements.iterator();
		while (itr.hasNext()) {
			SafeQueryEntitlement item = (SafeQueryEntitlement) itr.next();
			item.setPrivilegeId(privilegeId);
			item.setId(newQueryEntitlementId());
		}
		batchSaveQuery(queryEntitlements);
	}

	private void batchSaveQuery(Collection queryEntitlements){
		Iterator itr = queryEntitlements.iterator();
		while (itr.hasNext()) {
			SafeQueryEntitlement item = (SafeQueryEntitlement) itr.next();
			queryEntitlementMapper.insert(item);
		}
	}

	private boolean hasPrivilege(SafeUser user, String pvlgId) {
		SafePrivilege privilege = privilegeManager.getPrivilege(pvlgId);
		if (privilege == null) {
			String msg = "No privilege with id='" + pvlgId + "' found.";
			log.error(msg);
			throw new AuthException(msg);
		}
		UserRoleManagerService userRoleManager = (UserRoleManagerService) SpringUtils.getBean("userRoleManagerService");

		return userRoleManager.hasPrivilege(user.getId(), pvlgId);
	}

	public DecisionEntitlementTestResult testDecisionEntitlement(Locale locale, String privilegeId, List decisionEntitlements, SafeUser user, Map context) {
		try {
			SafePrivilege privilege = privilegeManager.getPrivilege(privilegeId);

			if (privilege == null) {
				DecisionEntitlementTestResult result = new DecisionEntitlementTestResult();
				Decision decision = new Decision();
				decision.setPermit(false);
				decision.setDenyReason("No privilege with id='" + privilegeId + "' found.");
				result.setDecision(decision);
				return result;
			}

			if (!hasPrivilege(user, privilegeId)) {
				DecisionEntitlementTestResult result = new DecisionEntitlementTestResult();
				Decision decision = new Decision();
				decision.setPermit(false);
				decision.setDenyReason(I18N.getAuthMessage(locale, Constants.NO_PRIVILEGE, privilege.getName()));
				result.setDecision(decision);
				return result;
			}

			if (privilege.getDecisionPolicyCombAlg() == SafePrivilege.DECISION_ORDERED_PERMIT_OVERRIDES) {
				return decisionOrderedPermitOverrides(decisionEntitlements, user, context);
			} else if (privilege.getDecisionPolicyCombAlg() == SafePrivilege.DECISION_ORDERED_DENY_OVERRIDES) {
				return decisionOrderedDenyOverrides(decisionEntitlements, user, context);
			} else if (privilege.getDecisionPolicyCombAlg() == SafePrivilege.DECISION_FIRST_APPLICABLE) {
				return decisionFirstApplicable(decisionEntitlements, user, context);
			} else {
				throw new AuthException(privilege.getDecisionPolicyCombAlg() + " is an invalid DecisionPolicyCombiningAlgorithm");
			}
		} catch (Exception e) {
			DecisionEntitlementTestResult result = new DecisionEntitlementTestResult();
			result.setFailed(true);
			result.setErrorMessage(e.getMessage());
			return result;
		}
	}

	public QueryEntitlementTestResult testQueryEntitlement(Locale locale, String privilegeId, List queryEntitlements, SafeUser user, Map context, int first, int max) {
		QueryEntitlementTestResult result = new QueryEntitlementTestResult();
		try {
			SafePrivilege privilege = privilegeManager.getPrivilege(privilegeId);

			if (privilege == null) {
				result.setFailed(true);
				result.setErrorMessage("No privilege with id='" + privilegeId + "' found.");
				return result;
			}

			if (!hasPrivilege(user, privilegeId)) {
				result.setFailed(true);
				result.setErrorMessage(I18N.getAuthMessage(locale, Constants.NO_PRIVILEGE_FOR_QUERY_POLICY, privilege.getName()));
				return result;
			}

			SafeQuery query = findQueryMatched(locale, queryEntitlements, user, context, result);
			if (result.isFailed()) {
				return result;
			} else {
				result.setMatchedQuery(query);
				QueryTestResult queryTestResult = query.test(user, context, first, max);
				result.setQueryTestResults(queryTestResult);

				if (queryTestResult.isFailed()) {
					result.setFailed(true);
					result.setErrorMessage(queryTestResult.getErrorMessage());
					return result;
				} else {
					result.setFailed(false);
					return result;
				}
			}

		} catch (Exception e) {
			result.setFailed(true);
			result.setErrorMessage(e.getMessage());
			return result;
		}
	}

	public SafeQuery getQuery(String privilegeId, SafeUser user, Map context) {
		SafeQuery query = findQueryMatched(privilegeId, user, context);
		if (query != null) {
			query = queryManager.cloneQuery(query);
		}
		return query;
	}

}
